/******************************************************************************
 *                                                                          
 * 
 * FILENAME
 *     Audio_Demo.c
 *
 * VERSION
 *     1.0 
 *
 * DESCRIPTION
 *
 *
 *
 *
 * DATA STRUCTURES
 *     None
 *
 * FUNCTIONS
 *
 *
 *     
 * HISTORY
 *     2004.08.04		Created by Yung-Chang Huang
 *
 *
 * REMARK
 *     None
 *
 **************************************************************************/
#ifdef ECOS
#include "stdio.h"
#include "stdlib.h"
#include "kapi.h"
#include "diag.h"
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wblib.h"
#endif


#include "wbio.h"
#include "NUC900_reg.h"
#include "NUC900_Audio.h"
#include "nvtfat.h"
#include "NUC900_SIC.h"

//#define TEST_AC97
#define TEST_I2S

//#define TEST_PLAYBACK

#define USEUART 1
#define CACHE_ON 1
#define REAL_CHIP 1

static const UINT32 PCM_QUEUE_LEN		=	(512*1024);

#if USEUART
#undef getchar
#ifdef ECOS
UINT32 UART_BA = UART0_BA;
#define getchar		diag_getc
#define printf		diag_printf
#else
#define getchar		sysGetChar
#define printf		sysprintf
#endif
#endif// end of #if USEUART

#ifdef ECOS
#define get_timer_ticks()   cyg_current_time()
#define sysprintf	diag_printf
#else
#define get_timer_ticks()	sysGetTicks(TIMER0)
#endif

__align(32) static UINT8 _pucPcmQueue [PCM_QUEUE_LEN];
UINT32 volatile _uPcmQHead, _uPcmQTail;


#ifndef ECOS
#include <rt_misc.h>

__value_in_regs struct __initial_stackheap __user_initial_stackheap(
	unsigned R0, unsigned SP, unsigned R2, unsigned SL)
{
	struct __initial_stackheap config;
	config.heap_base = 0x00440000;
	config.heap_limit = 0x800000;
	config.stack_base = 0xc00000;
	config.stack_limit = 0x800000;
	return config;
}
#endif

INT  play_callback(UINT8 *pucBuff, UINT32 uDataLen)
{
	INT		nLen;
	
	nLen = PCM_QUEUE_LEN - _uPcmQHead;
	
	if (nLen >= uDataLen)
	{
		memcpy(pucBuff, &_pucPcmQueue[_uPcmQHead], uDataLen);
		_uPcmQHead = (_uPcmQHead + uDataLen) % PCM_QUEUE_LEN;
	}
	else
	{
		memcpy(pucBuff, &_pucPcmQueue[_uPcmQHead], nLen);
		memcpy(&pucBuff[nLen], _pucPcmQueue, uDataLen - nLen);
		_uPcmQHead = uDataLen - nLen;

	}
	
	return 0;
}

INT  record_callback(UINT8 *pucBuff, UINT32 uDataLen)
{
	INT		nLen;
		
	nLen = PCM_QUEUE_LEN - _uPcmQTail;
	if (nLen >= uDataLen)
	{
		memcpy(&_pucPcmQueue[_uPcmQTail], pucBuff, uDataLen);
		_uPcmQTail = (_uPcmQTail + uDataLen) % PCM_QUEUE_LEN;
	}
	else
	{
		memcpy(&_pucPcmQueue[_uPcmQTail], pucBuff, nLen);
		memcpy(_pucPcmQueue, &pucBuff[nLen], uDataLen - nLen);
		_uPcmQTail = uDataLen - nLen;
	}
	return 0;
}

#ifdef ECOS
void audio_demo (cyg_addrword_t tdata)
#else
void audio_demo(void)
#endif
{
	INT		hFile;
	INT 	nErr;
	INT8	bAsciiFileName [20];
	INT8	bUnicodeFileName [40];
	INT		nReadBytes;
	INT		nTime0,ntemp;
	UINT32  nvolume;
	WAVEFORMATEX palyformatTex;
	AU_DEV_E edev;
	
	/* Test case */
	INT choose = AU_SAMPLE_RATE_44100;
	INT channels = 2;
	INT format = IIS_FORMAT;
	/*************/
	
	_uPcmQHead = _uPcmQTail = 0;

	switch(choose){
	case 48000:
	strcpy( bAsciiFileName,"C:\\48k.pcm");break;
	case 44100:
	strcpy(bAsciiFileName , "C:\\44.1k.pcm");break;
	case 32000:
	strcpy(bAsciiFileName , "C:\\32k.pcm");break;
	case 24000:
	strcpy(bAsciiFileName , "C:\\24k.pcm");break;
	case 22050:
	strcpy(bAsciiFileName , "C:\\22.05k.pcm");break;
	case 16000:
	strcpy(bAsciiFileName , "C:\\16k.pcm");break;
	case 12000:
	strcpy(bAsciiFileName , "C:\\12k.pcm");break;
	case 11025:
	strcpy(bAsciiFileName , "C:\\11.025k.pcm");break;
	case 8000:
	strcpy(bAsciiFileName , "C:\\8k.pcm");break;
	}
	fsAsciiToUnicode(bAsciiFileName, bUnicodeFileName,1);
	//fsNativeLangToUnicode(bAsciiFileName, bUnicodeFileName);
	hFile = fsOpenFile(bUnicodeFileName, bAsciiFileName,OP_READ);
	//hFile = fsOpenFile(bAsciiFileName, OP_READ);
	
	if (hFile < 0)
	{
		printf("\nCan't open playback file 0x%x!\n",hFile);
		//return;
	}
	
	nErr = fsReadFile(hFile, _pucPcmQueue, PCM_QUEUE_LEN, &nReadBytes);	
	printf("Read %d KB to PCM data buffer...\n", nReadBytes/1024);
	if (nErr<0)
		printf("err num=%d\n",nErr);
	printf("OK\n");
	
	
	palyformatTex.sChannels = (UINT32)channels;
	palyformatTex.nSamplesPerSec = (UINT32)choose;
	palyformatTex.fnWAVCallBack = play_callback;

	
#ifdef TEST_AC97
    edev = AU_DEV_AC97;
#else
    edev = AU_DEV_I2S;
#endif        	
	WAV_Open(WAPI_OUT,&palyformatTex,edev);

#ifndef TEST_AC97
	nvolume = 0xa000a;
    WAV_IOControl(WAPI_OUT,WPDM_SETPLAYVOLUME,(UINT32)&nvolume,NULL,edev);
#endif
    
    WAV_IOControl(WAPI_OUT,WPDM_START,(UINT32)&palyformatTex,NULL,edev);      
    
	ntemp = nTime0 = get_timer_ticks();
	while (1)
	{

		if (get_timer_ticks() - nTime0 >= 100)
		{
			nTime0 = get_timer_ticks();
			printf("H=%d, T=%d\n", _uPcmQHead/1024, _uPcmQTail/1024);
			
		}
		
		if ((_uPcmQTail > _uPcmQHead) ||
			((_uPcmQTail < _uPcmQHead) && (_uPcmQHead - _uPcmQTail > 1024)))
		{
			fsReadFile(hFile, &_pucPcmQueue[_uPcmQTail], 1024, &nReadBytes);
			
			if (nReadBytes > 0)
				_uPcmQTail = (_uPcmQTail + nReadBytes) % PCM_QUEUE_LEN;
				
			//if (get_timer_ticks() - ntemp >=21200)
			if (get_timer_ticks() - ntemp >=1000)
				break;
		    /*
			if (get_timer_ticks() - ntemp ==1572)
			{
				outpw(REG_ACTL_RESET,inpw(REG_ACTL_RESET) | (1<<3));//zero crossing detect enable
			}
			if (get_timer_ticks() - ntemp ==2000)
			{
				outpw(REG_ACTL_RESET,inpw(REG_ACTL_RESET) & ~(1<<3));//zero crossing detect disable
			}*/
		}		

	}
	fsCloseFile(hFile);
	//WAV_IOControl(WAPI_OUT,WPDM_CLOSE,NULL,NULL,edev);//can't close when using I2S CODEC PCM3003
	WAV_IOControl(WAPI_OUT,WPDM_STOP,NULL,NULL,edev);
	
}

#ifdef ECOS
void audio_loopback_demo (cyg_addrword_t tdata)
#else
void  audio_loopback_demo(void)
#endif
{
	INT		nTime0,nTime1,_ntempHead,_ntempTail;
	UINT32  nvolume;
	WAVEFORMATEX playformatTex;
	WAVEFORMATEX recformatTex;
	AU_DEV_E edev;

	/* Test case */	
	INT choose = AU_SAMPLE_RATE_48000;
	INT ch = 2;
	INT format = IIS_FORMAT;
	/************/
	
	_uPcmQHead = _uPcmQTail = 0;
    playformatTex.sChannels = (UINT16)ch;
	playformatTex.nSamplesPerSec = (UINT32)choose;
	playformatTex.fnWAVCallBack = play_callback;
    
    recformatTex.sChannels = (UINT16)ch;
	recformatTex.nSamplesPerSec = (UINT32)choose;
	recformatTex.fnWAVCallBack = record_callback;
    
	
#ifdef TEST_AC97
    edev = AU_DEV_AC97;
#else
    edev = AU_DEV_I2S;
#endif        	
	if (WAV_Open(WAPI_OUT,&playformatTex,edev)<0)
	    return ;
	    
	if (WAV_Open(WAPI_IN,&recformatTex,edev)<0)
	    return ;

	while(1)
	{
		printf("Start record...\n");
#ifndef TEST_AC97
		nvolume = 0x50005;
    	WAV_IOControl(WAPI_IN,WPDM_SETRECVOLUME,(UINT32)&nvolume,NULL,edev);
#endif
		WAV_IOControl(WAPI_IN,WPDM_START,(UINT32)&recformatTex,NULL,edev);
		
		nTime0 = get_timer_ticks();
		while ((get_timer_ticks()-nTime0) < 200);//delay 2 sec
		WAV_IOControl(WAPI_IN,WPDM_STOP,(UINT32)&recformatTex,NULL,edev);
		//WAV_Close(WAPI_IN, edev);
				
		printf("Start play...\n");
#ifndef TEST_AC97
		nvolume = 0x110011;
    	WAV_IOControl(WAPI_OUT,WPDM_SETPLAYVOLUME,(UINT32)&nvolume,NULL,edev);
#endif
    	WAV_IOControl(WAPI_OUT,WPDM_START,(UINT32)&playformatTex,NULL,edev);
		
		nTime0 = nTime1 = get_timer_ticks();
		_ntempHead = _uPcmQHead;
		_ntempTail = _uPcmQTail;
		while (1)
		{
    		if ((get_timer_ticks() - nTime0) >= 100)
			{
				if (_ntempHead!=_uPcmQHead)
					_ntempHead = _uPcmQHead;

				if (_ntempTail!=_uPcmQTail)
					_ntempTail = _uPcmQTail;

				nTime0 = get_timer_ticks();
				printf("H=%d, T=%d\n", _uPcmQHead/1024, _uPcmQTail/1024);
			}
			if ((get_timer_ticks() - nTime1) >=1500)
			{
				break;
			}
		}
		break;
	}
}


void record_to_file()
{
	INT		hFile;
	INT		nWriteBytes, nTotalBytes = 0;
	INT		nTime0;
	
	_uPcmQHead = _uPcmQTail = 0;

	hFile = fsOpenFile("C:\\record.pcm", NULL,  O_CREATE);
	
	printf("Start record...\n");
	
	nTime0 = get_timer_ticks();
	while (1)
	{
		if (get_timer_ticks() - nTime0 >= 100)
		{
			nTime0 = get_timer_ticks();
			printf("H=%d, T=%d\n", _uPcmQHead/1024, _uPcmQTail/1024);
		}
		
		if ((_uPcmQHead > _uPcmQTail) ||
			((_uPcmQHead < _uPcmQTail) && (_uPcmQTail - _uPcmQHead > 8192)))
		{
			fsWriteFile(hFile, &_pucPcmQueue[_uPcmQHead], 8192, &nWriteBytes);
			
			if (nWriteBytes > 0)
				_uPcmQHead = (_uPcmQHead + nWriteBytes) % PCM_QUEUE_LEN;
			else
				break;
				
			nTotalBytes += nWriteBytes;
			
			if (nTotalBytes >= 16 * 1000 * 30)
				break;
		}
	}
	fsCloseFile(hFile);
}

#ifdef ECOS
static cyg_handle_t  	thread_handle;
static cyg_thread 		thread;
#define STACKSIZE		(128*1024)
static UINT8            _Statck[STACKSIZE];
#endif

int main()
{
	INT i;
	WB_PLL_T sysClock;
	
	sysClock.pll0 = PLL_200MHZ;					//PLL0 output clock
	sysClock.pll1 = PLL_100MHZ;					//PLL1 output clock
	sysClock.cpu_src = CPU_FROM_PLL0;		//Select CPU clock source
	sysClock.ahb_clk = AHB_CPUCLK_1_2;	//Select AHB clock divider
	sysClock.apb_clk = APB_AHB_1_2;			//Select APB clock divider
	sysSetPLLConfig(&sysClock);					//Call system function call	

#if CACHE_ON
	sysDisableCache();
  	sysInvalidCache();
	sysEnableCache(CACHE_WRITE_BACK);
#endif

#ifndef ECOS
#if USEUART
    {
	WB_UART_T uart;
	uart.uiFreq = 15000000;
	//uart.uiFreq = 12288000;
    uart.uiBaudrate = 115200;
    uart.uiDataBits = WB_DATA_BITS_8;
    uart.uiStopBits = WB_STOP_BITS_1;
    uart.uiParity = WB_PARITY_NONE;
    uart.uiRxTriggerLevel = LEVEL_1_BYTE;
    sysInitializeUART(&uart);
    }
    outpw(REG_MFSEL,inpw(REG_MFSEL) | 0x1f00);
#endif
    
    //=== init timer0 for FMI === 
    sysSetTimerReferenceClock(TIMER0, 15000000);
	sysStartTimer(TIMER0, 100, PERIODIC_MODE);
#endif
	
	fsInitFileSystem();
	fmiSetFMIReferenceClock(200000);
	fmiInitDevice();
	fmiInitSDDevice(FMI_SD_CARD0);

#ifdef ECOS
#ifdef TEST_PLAYBACK
	cyg_thread_create(22, audio_loopback_demo, 0, "audio", _Statck, STACKSIZE, &thread_handle, &thread);
#else
	cyg_thread_create(22, audio_demo, 0, "audio", _Statck, STACKSIZE, &thread_handle, &thread);
#endif
	cyg_thread_resume(thread_handle);

#else	
#ifdef TEST_PLAYBACK
	audio_loopback_demo();		
#else
	audio_demo();
#endif
#endif
}

#ifndef ECOS
/*
 * standalone.c - minimal bootstrap for C library
 * Copyright (C) 2000 ARM Limited.
 * All rights reserved.
 */

/*
 * RCS $Revision: 1 $
 * Checkin $Date: 08/08/22 7:33p $ 0
 * Revising $Author: Mncheng $
 */

/*
 * This code defines a run-time environment for the C library.
 * Without this, the C startup code will attempt to use semi-hosting
 * calls to get environment information.
 */
void _sys_exit(int return_code)
{
label:  goto label; /* endless loop */
}

void _ttywrch(int ch)
{
    char tempch = (char)ch;
    (void)tempch;
}
/* end of file standalone.c */

/* end of file standalone.c */
#endif


